<?php
/**
 * Zend
 *
 * to provide the Zend Components
 *
 *
 */

class Zend
{
    const ERROR_INTERNAL = 500;

    /**
     * To force to get singleton instance
     */
    static private $_instance = null;

    private function __construct() {}

    private function __clone() {}

    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    /**
     * Class Loader
     *
     */
    public static function initLoader()
    {
        $config = self::initConfig('Loader');
        if (!include_once('Extension/Loader.php')) {
            $errorMessage = $config->errorMessage;
            throw new Exception($errorMessage, self::ERROR_INTERNAL);
        } else {
            Extension_Loader::registerAutoload($config->loaderClass, (bool)$config->registerAutoload);
        }
    }

    /**
     * Configrations
     * @todo config should be cache
     *
     */
    public static function initConfig($filename, $type = 'ini', $suffix = '_Config', $section = null, $options = false)
    {
        if ($type != 'ini' and $type != 'xml') {
            throw new Exception('unknown type of config file !', self::ERROR_INTERNAL);
        }

        try {
            require_once('Zend/Registry.php');

            $configName = $filename . $suffix;

            if (!Zend_Registry::isregistered($configName)) {

                if (!defined('PROJECT_ENVIRONMENT')) {
                    defined('PROJECT_ENVIRONMENT', Bootstrap::PRODUCTION);
                }

                if (null === $section) {
                    $section = PROJECT_ENVIRONMENT;
                }

                if ('ini' === $type) {
                    require_once('Zend/Config/Ini.php');
                    $config = new Zend_Config_Ini($filename . '.ini', $section, $options);
                } else {
                    require_once('Zend/Config/Xml.php');
                    $config = new Zend_Config_Xml($filename . '.xml', $section, $options);
                }

                Zend_Registry::set($configName, $config);

                return $config;

            } else {
                return Zend_Registry::get($configName);
            }

        } catch (Exception $e) {

            throw new Exception($e->getMessage(), self::ERROR_INTERNAL);
        }
    }

    /**
     * Controller
     *
     */
    public static function initController()
    {
        $config = self::initConfig('Controller/Front');
        
        $front = Zend_Controller_Front::getInstance();

        $front->addModuleDirectory( PROJECT_ROOT . $config->moduleDirectory )
              ->setModuleControllerDirectoryName( $config->moduleControllerDirectoryName )
              ->setDefaultModule( $config->defaultModule )
              ->setParam('noErrorHandler', $config->noErrorHandler)
              ->throwExceptions((bool) $config->throwExceptions)
              ->returnResponse((bool) $config->returnResponse);
    }

    /**
     * Plugins
     * All functionalities and plugins are here !
     *
     */
    public static function initPlugin($section = 'common')
    {
        $config = self::initConfig('Controller/Plugin', 'ini');

        if (null != $config->$section) {

            $front = Zend_Controller_Front::getInstance();

            if (null != $config->$section->name) {

                $plugins = $config->$section->name->toArray();

                foreach ($plugins as $name => $stackIndex) {

                    $plugin = $config->$section->prefix . $name;

                    if (class_exists($plugin)) {

                        if (false == Zend_Controller_Front::getInstance()->hasPlugin($plugin)) {

                            $front->registerPlugin(new $plugin, $stackIndex);
                        }
                    }
                }
            }
        }
    }

    /**
     * Helpers
     *
     */
    public static function initHelper($section = 'common')
    {
        $config = self::initConfig('Controller/Helper');

        if (null != $config->$section and null != $config->$section->name) {

            $helpers = $config->$section->name->toArray();

            foreach ($helpers as $name => $stackIndex) {

                $helper = $config->$section->prefix . $name;

                if (class_exists($helper) and !Zend_Controller_Action_HelperBroker::hasHelper($name)) {

                    Zend_Controller_Action_HelperBroker::getStack()->offsetSet($stackIndex, new $helper);
                }
            }
        }
    }

    /**
     * Database Connection
     *
     */
    public static function initDb()
    {
        if (!Zend_Registry::isregistered('db')) {

            $config = self::initConfig('Db');
            $options = array(Zend_Db::AUTO_QUOTE_IDENTIFIERS => false,
                             PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true);
            $db = Zend_Db::factory($config->db, array('options' => $options));

            if ($db->getConnection()) {
                $db->query("SET NAMES '{$config->db->charset}'");
                Zend_Registry::set('db', $db);
                return $db;
            } else {
                throw new Zend_Db_Exception($config->noConnectMessage);
            }

        } else {
            return Zend_Registry::get('db');
        }
    }

    /**
     * Session
     *
     */
    public static function initSession($namespace = false, $options = array())
    {
        $config = self::initConfig('Session');

        if (!$options and (null != $config->options) and is_array($config->options->toArray())) {
            Extension_Session::setOptions($config->options->toArray());
        } elseif (is_array($options)) {
            Extension_Session::setOptions($options);
        }

        if ($config->regenerate_id) {
            Extension_Session::regenerateId();
        }

        Extension_Session::start();

        if (!$namespace) {
            $session = new Extension_Session_Namespace($config->namespace->default);
            if ($config->default->expirationHops) {
                $session->setExpirationHops($config->default->expirationHops);                
            }
            if ($config->default->expirationSeconds) {
                $session->setExpirationSeconds($config->default->expirationSeconds);
            }
        } else {
            $session = new Extension_Session_Namespace((string) $namespace);
        }

        return $session;
    }

    /**
     * Locale
     *
     */
    public static function initLocale()
    {
        if (false == Zend_Registry::isRegistered('Zend_Locale')) {

            $config = Zend::initConfig('Locale');

            $namespace = Zend::initConfig('Session')->namespace->locale;
            $session = Zend::initSession($namespace);

            Extension_Locale::$compatibilityMode = false;

            $cache = self::initCache('Locale');
            if ($cache instanceof Zend_Cache_Core) {
                Extension_Locale::setCache($cache);
            }

            if (null != $session->locale) {
                $locale = new Extension_Locale($session->locale);
            } elseif (null !== $config->default and Extension_Locale::isLocale($config->default, Extension_Locale::NOT_STRICT)) {
                $locale = new Extension_Locale($config->default);
            } else {
                $locale = new Extension_Locale(Extension_Locale::BROWSER);
            }

            Zend_Registry::set('Zend_Locale', $locale);
            return $locale;

        } else {
            return Zend_Registry::get('Zend_Locale');
        }
    }

    /**
     * Date
     *
     */
    public static function initDate()
    {
        if (false == Zend_Registry::isRegistered('Zend_Date')) {

            $config = Zend::initConfig('Date');

            $options = array(
                'format_type'   =>  $config->format_type,
                'fix_dst'       =>  (bool) $config->fix_dst,
                'extend_month'  =>  (bool) $config->extend_month,
            );

            $date = Zend_Date::now();
            $date->setOptions($options);
            $date->setLocale(self::initLocale());
            $date->setTimezone($config->timezone);

            Zend_Registry::set('Zend_Date', $date);
            return $date;

        } else {

            $date = Zend_Registry::get('Zend_Date');
            $date->set(Zend_Date::now());
            return $date;
        }
    }

    /**
     * Cache
     *
     */
    public static function initCache($name = 'Default', $prefix = '')
    {
        $config = self::initConfig('Cache');

        if ($config->enable and isset($config->$name) and $config->$name->enable) {

            $cacheName = 'Zend_Cache_' . $name;

            if (false == Zend_Registry::isregistered($cacheName)) {

                $backendOptions = $config->$name->backend->options->toArray();

                if (!empty($prefix)) {
                    $backendOptions['file_name_prefix'] = $prefix;
                }

                $cache = Zend_Cache::factory(
                    $config->$name->frontend->type, 
                    $config->$name->backend->type, 
                    $config->$name->frontend->options->toArray(), 
                    $backendOptions
                );

                Zend_Registry::set($cacheName, $cache);
                return $cache;

            } else {
                return Zend_Registry::get($cacheName);
            }
        } else {
            return false;
        }
    }

    /**
     * Render output
     *
     */
    public static function output()
    {
        $response = Zend_Controller_Front::getInstance()->dispatch();
        echo $response;
    }

    /**
     * Handle the exceptions
     *
     */
    public static function handleException(Exception $e)
    {
        ini_set('error_reporting', E_ALL);

        $config = self::initConfig('Exception');

        if ($e instanceof Zend_Exception) {
            
            if (!@include_once('Error/Kbs/404.html')) {
                @header('HTTP/1.x 404 Not Found');
                @header('Status: 404 Not Found');
            }

            $message = $e->getMessage() . PHP_EOL;

            if ($config->print->enable) {
                $errorMessage = $message;
                $errorMessage .= $config->print->trace ? ($e->getTraceAsString() . PHP_EOL) : '';
                Zend_Debug::dump($errorMessage, $config->print->label);
            }
            
            require_once('Zend/Log.php');
            $logger = new Zend_Log();

            if ($config->log->file->enable) {
                
                $errorMessage = $message;
                $errorMessage .= $config->log->file->trace ? ($e->getTraceAsString() . PHP_EOL) : '';
                require_once('Zend/Log/Writer/Stream.php');

                $writerFile = new Zend_Log_Writer_Stream(PROJECT_ROOT . $config->log->file->path);
                $writerFile->addFilter(Zend_Log::DEBUG);
                $formatter = $config->log->file->formatter;
                $writerFile->setFormatter(new $formatter);
                $logger->addWriter($writerFile);
            }

            if ($config->log->db->enable) {
                $errorMessage = $message;
                $errorMessage .= $config->log->db->trace ? ($e->getTraceAsString() . PHP_EOL) : '';
                $columnMapping = array($config->log->db->column->priority     => 'priority', 
                                       $config->log->db->column->message      => 'message',
                                       $config->log->db->column->timestamp    => 'timestamp');
                require_once('Zend/Log/Writer/Db.php');
                $writerDb = new Zend_Log_Writer_Db(Zend::initDb(), $config->log->db->table, $columnMapping);
                $writerDb->addFilter(Zend_Log::DEBUG);
                
                $date = self::initDate();
                $resultDate = $date->toString($config->log->db->column->timestampFormat);

                $logger->setEventItem('timestamp', $resultDate);
                $logger->addWriter($writerDb);
            }

            if ($config->log->firebug->enable) {
                /**
                 * @todo firebug !
                 */
                $errorMessage = $message;
                $errorMessage .= $config->log->file->trace ? ($e->getTraceAsString() . PHP_EOL) : '';
                require_once('Zend/Log/Writer/Firebug.php');
                $writerFirebug = new Zend_Log_Writer_Firebug();
                $logger->addWriter($writerFirebug);
            }

            if ($config->log->file->enable or $config->log->db->enable or $config->log->firebug->enable) {
                $logger->log($errorMessage, Zend_Log::DEBUG);
            }

            $logger = null;

        } else {

            switch ($e->getCode()) {
                case self::ERROR_INTERNAL:
                default:
                    if (!include_once('Error/Kbs/500.html')) {
                        @header("HTTP/1.x 500 Internal Server Error");
                        @header('Status: 500 Internal Server Error');
                    }

                    $errorMessage = $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL . PHP_EOL;

                    if (PROJECT_ENVIRONMENT != Bootstrap::PRODUCTION) {
                        echo '<br /><br /><hr>';
                        echo nl2br(print_r($errorMessage, true));
                    }
                     
                    $errorLog = PROJECT_ROOT . '/Repository/Logs/Error.log';
                    @file_put_contents($errorLog, $errorMessage, FILE_APPEND);
                    break;
            }

        }

        exit();
    }


}